package service

import (
	"github.com/gin-gonic/gin"
	"gluenet/cmd/glue-srvm/app"
	"gluenet/internal/glue-srvm/model"
	"gluenet/internal/glue-srvm/pkg/gluehttp"
	"gluenet/internal/glue-srvm/pkg/gluelog"
	"gluenet/internal/glue-srvm/storages"
	apis2 "gluenet/pkg/apis"
	"gluenet/pkg/database/mysql"
	"gorm.io/gorm"
	"strconv"
)

const (
	MachinePermissionStatusNormal = iota + 1
	MachinePermissionStatusArrearage
)

const (
	ChargeTypeDemand = iota + 1
)

const (
	UseType = iota + 1
)

func GetAccount(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		db := mysql.GetDB()
		accountID := c.Query("account_id")
		id, _ := strconv.Atoi(accountID)
		account, err := storages.DefaultAccountStorage.FindOne(db, model.MeteringAccount{Id: uint64(id)})
		if err != nil {
			gluelog.Errorf("查询账户信息失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		gluehttp.Response(c, account)
	}
}

func ExpensePage(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		db := mysql.GetDB()
		var req model.ExpenseReq
		if err := c.ShouldBind(&req); err != nil {
			gluelog.Errorf("参数绑定失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		accountID := c.Query("account_id")
		expense, page, err := storages.DefaultExpenseStorage.FindPage(db, req, accountID)
		if err != nil {
			gluelog.Errorf("查询费用信息失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		expenseResp := make([]model.MeteringExpenseResp, len(expense))
		for i, v := range expense {
			var resourceType, chargeType, useType string
			if v.ResourceType == MachineTypeVMHost {
				resourceType = "虚拟机"
			}
			if v.ChargeType == ChargeTypeDemand {
				chargeType = "按需计费"
			}
			if v.UseType == UseType {
				useType = "时长"
			}
			expenseResp[i] = model.MeteringExpenseResp{
				Id:                   v.Id,
				ExpenseId:            v.ExpenseId,
				ResourceName:         v.ResourceName,
				ResourceType:         resourceType,
				ChargeType:           chargeType,
				UseType:              useType,
				Usage:                v.Usage,
				ConsumptionStartTime: v.ConsumptionStartTime,
				ConsumptionEndTime:   v.ConsumptionEndTime,
				BillingTime:          v.BillingTime,
				ConsumptionAmount:    v.ConsumptionAmount,
				AmountOwed:           v.AmountOwed,
				CreatedAt:            v.CreatedAt,
				UpdatedAt:            v.UpdatedAt,
				DeletedAt:            v.DeletedAt,
			}
		}
		gluehttp.Response(c, model.ExpensePageResp{
			Page: *page,
			Data: expenseResp,
		})
	}
}

func Recharge(app *app.Application) gin.HandlerFunc {
	return func(c *gin.Context) {
		var err error
		var tx *gorm.DB
		defer func() {
			if err != nil && tx != nil {
				tx.Rollback()
			}
		}()
		db := mysql.GetDB()
		var req model.RechargeOrderReq
		if err := c.ShouldBindJSON(&req); err != nil {
			gluelog.Errorf("参数绑定失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		tx = db.Begin()
		// 1.Generate Order
		orderID, err := storages.DefaultRechargeOrderStorageStorage.Insert(db, model.MeteringRechargeOrder{
			UserName:            "admin",
			Amount:              req.Amount,
			ItemName:            "测调币",
			MerchantName:        "测调",
			Acquirer:            "银行",
			ClearingInstitution: "银行",
			PaymentMethod:       1,
			TransactionNum:      "",
			PaymentStatus:       1,
		})
		if err != nil {
			gluelog.Errorf("生成订单失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		// 2.Update account
		account, err := storages.DefaultAccountStorage.FindOne(db, model.MeteringAccount{Id: req.Id})
		if err != nil {
			gluelog.Errorf("查询账户失败", err.Error())
			gluehttp.Response(c, apis2.ResponseReqErr(err, nil))
			return
		}
		if req.Amount >= account.Arrears {
			account.Balance = req.Amount - account.Arrears
			account.Arrears = 0
			// If the account is not in arrears, update the account and machine association table
			if account.Arrears <= 0 {
				err = storages.DefaultAccountToHostStorage.Update(db, model.MeteringAccountToHost{AccountId: account.Id}, model.MeteringAccountToHost{
					Status: MachinePermissionStatusNormal,
				})
				if err != nil {
					gluelog.Errorln("更新账户与机器关联表失败", "err:", err)
					return
				}
			}
		} else {
			account.Arrears = account.Arrears - req.Amount
		}
		err = storages.DefaultAccountStorage.Update(db, model.MeteringAccount{Id: req.Id}, *account)
		// 3.Generate account order association table
		_, err = storages.DefaultAccountToRechargeOrderStorage.Insert(db, model.MeteringAccountToRechargeOrder{
			AccountId:       account.Id,
			RechargeOrderId: uint64(orderID),
		})
		tx.Commit()
		gluehttp.Response(c, nil)
	}
}

func MeteringRouter(router *gin.RouterGroup) func(app *app.Application) {
	return func(app *app.Application) {
		v1 := router.Group("/metering")
		{
			v1.GET("/account", GetAccount(app))
			v1.GET("/expense/page", ExpensePage(app))
			v1.POST("/recharge", Recharge(app))
		}
	}
}

func init() {
	app.Router(MeteringRouter)
}
